﻿///<reference path="selector.js" />
(function() {

		var $S = amp.Cache.Selector;
		var $D = Sys.UI.DomElement;

		amp.byId = function(elementId) {
			return amp.isString(elementId) ? document.getElementById(elementId) : elementId;
		}


		amp.UI.Offset = function(left, top) {
			this.left = left;
			this.top = top;
			this[0] = left;
			this[1] = top;
		}

		amp.UI.Size = function(width, height) {
			this.width = width;
			this.height = height;
			this[0] = width;
			this[1] = height;
		}

		amp["class"]("amp.UI.Offset", null, amp.UI.Offset);
		amp["class"]("amp.UI.Size", null, amp.UI.Size);

		var scripts = {
			fragment: '<script[^>]*>([\\S\\s]*?)<\/script>',

			extract: function(content) {
				var all = new RegExp(this.fragment, 'img'),
				one = new RegExp(this.fragment, 'im');
				return (content.match(all) || []).map(function(tag) {
					return (tag.match(one) || ['', ''])[1];
				});
			},

			strip: function(content) {
				return content.replace(new RegExp(this.fragment, 'img'), "");
			},

			eval: function(content) {
				return this.extract(content).map(function(script) { return eval(script) });
			}
		}

		var Insertions = {
			before: function(element, node) {
				element.parentNode.insertBefore(node, element);
			},
			top: function(element, node) {
				element.insertBefore(node, element.firstChild);
			},
			bottom: function(element, node) {
				element.appendChild(node);
			},
			after: function(element, node) {
				element.parentNode.insertBefore(node, element.nextSibling);
			},
			tags: {
				TABLE: ['<table>', '</table>', 1],
				TBODY: ['<table><tbody>', '</tbody></table>', 2],
				THEAD: ['<table><tbody>', '</tbody></table>', 2],
				TFOOT: ['<table><tbody>', '</tbody></table>', 2],
				TR: ['<table><tbody><tr>', '</tr></tbody></table>', 3],
				TD: ['<table><tbody><tr><td>', '</td></tr></tbody></table>', 4],
				TH: ['<table><tbody><tr><td>', '</td></tr></tbody></table>', 4],
				SELECT: ['<select>', '</select>', 1]
			}
		}

		if (!window.Node)
			window.Node = {};

		if (!window.Node.ELEMENT_NODE) {
			// DOM level 2 ECMAScript Language Binding
			amp.extend(window.Node, {
				ELEMENT_NODE: 1,
				ATTRIBUTE_NODE: 2,
				TEXT_NODE: 3,
				CDATA_SECTION_NODE: 4,
				ENTITY_REFERENCE_NODE: 5,
				ENTITY_NODE: 6,
				PROCESSING_INSTRUCTION_NODE: 7,
				COMMENT_NODE: 8,
				DOCUMENT_NODE: 9,
				DOCUMENT_TYPE_NODE: 10,
				DOCUMENT_FRAGMENT_NODE: 11,
				NOTATION_NODE: 12
			});
		}
		var Element = {
			attributeTranslations: {
				write: {
					names: {
						className: 'class',
						htmlFor: 'for'
					},
					values: {}
				}
			},
			cache: {},

			create: function(tagName, attributes) {

				attributes = attributes || {};
				tagName = tagName.toLowerCase();

				if (amp.Browser.Msie && attributes.name) {
					tagName = '<' + tagName + ' name="' + attributes.name + '">';
					delete attributes.name;
					return Element.writeAttribute(document.createElement(tagName), attributes);
				}
				if (!Element.cache[tagName])
					Element.cache[tagName] = Element.extend(document.createElement(tagName));
				return Element.writeAttribute(cache[tagName], attributes);
			},

			evalScripts: function(value) {
				var temp = function() { scripts.eval(value) }
				temp.bindContext(value).defer();
			},

			replace: function(element, value) {
				element = amp.byId(element);
				value = amp.string(value);
				var range = element.ownerDocument.createRange();
				range.selectNode(element);
				Element.evalScripts(value);
				value = range.createContextualFragment(scripts.strip(value));
				element.parentNode.replaceChild(value, element);
			},

			remove: function(element) {
				element.parentNode.removeChild(amp.byId(element));
			},

			setHtml: function(element, value) {
				element.innerHTML = value;
			},

			getContentFromAnonymousElement: function(tagname, html) {
				var div = document.createElement('div'), t = Insertions.tags[tagname];
				if (t) {
					div.innerHTML = t[0] + html + t[1];
					for (var i = 0; i < t[2]; i++)
						div = div.firstChild;
				} else div.innerHTML = html;
				return amp.array(div.childNodes);
			},


			recursivelyCollect: function(element, property) {
				element = amp.byId(element);
				var elements = [];
				while (element = element[property])
					if (element.nodeType == 1)
					elements.push(element);
				return elements;
			},

			ancestors: function(element) {
				return this.recursivelyCollect(element, 'parentNode');
			},

			descendants: function(element) {
				return $S.query("*", element);
			},

			firstDescendant: function(element) {
				element = amp.byId(element).firstChild;
				while (element && element.nodeType != 1) element = element.nextSibling;
				return element;
			},

			immediateDescendants: function(element) {
				element = amp.byId(element)
				if (!(element = element.firstChild)) return [];
				while (element && element.nodeType != 1) element = element.nextSibling;
				if (element) return [element].concat(element.nextSiblings());
				return [];
			},

			previousSiblings: function(element) {
				return this.recursivelyCollect(element, 'previousSibling');
			},

			nextSiblings: function(element) {
				return this.recursivelyCollect(element, 'nextSibling');
			},

			siblings: function(element) {
				return this.previousSiblings(element).reverse().concat(this.nextSiblings(element));
			},

			update: function(element, value) {
				value = amp.string(value);
				Element.setHtml(element, value);
				Element.evalScripts(value);
				return this;
			},

			insert: function(element, insertions) {
				element = amp.byId(element);

				if (amp.isString(insertions) || amp.isNumber(insertions))
					insertions = { bottom: insertions };

				var content, insert, tagName, childNodes;

				for (var position in insertions) {
					content = insertions[position];
					position = position.toLowerCase();
					insert = Insertions[position];
					content = amp.string(content);
					tagName = ((position == 'before' || position == 'after') ? element.parentNode : element).tagName.toUpperCase();
					childNodes = Element.getContentFromAnonymousElement(tagName, scripts.strip(content));

					if (position == 'top' || position == 'after')
						childNodes.reverse();

					childNodes.each(insert.curry(element));
					Element.evalScripts(content);
				}

				return element;
			},


			scrollTo: function(element) {
				var pos = this.cumlativeOffset(element)
				window.scrollTo(pos.top, pos.left);
			},

			counter: 0,

			identify: function(element) {
				var id = this.readAttribute(element, 'id'), self = this;
				if (id)
					return id;

				do { id = 'amp_element_' + self.counter++ } while (amp.byId(id));

				this.writeAttribute(element, 'id', id);
				return id;
			},

			select: function(element, selector) {
				return $S.query(selector, element);
			},

			cumulativeOffset: function(element) {
				var valueT = 0, valueL = 0;
				do {
					valueT += element.offsetTop || 0;
					valueL += element.offsetLeft || 0;
					element = element.offsetParent;
				} while (element);

				return new amp.UI.Offset(valueL, valueT);
			},


			readAttribute: function(element, name) {
				element = amp.byId(element);
				if (amp.Browser.Msie) {
					var t = attributeTranslations.read;
					if (t.values[name])
						return t.values[name](element, name);
					if (t.names[name]) name = t.names[name];
					if (name.indexOf(":") > -1)
						return (!element.attributes || !element.attributes[name]) ? null : element.attributes[name].value;
				}
				return element.getAttribute(name);
			},

			writeAttribute: function(element, name, value) {
				element = amp.by(element);
				var attributes = {}, t = attributeTranslations.write;

				if (typeof name == 'object')
					attributes = name;
				else
					attributes[name] = amp.isUndefined(value) ? true : value;

				for (var attr in attributes) {
					name = t.names[attr] || attr;
					value = attributes[attr];
					if (t.values[attr]) name = t.values[attr](element, value);
					if (value === false || value === null)
						element.removeAttribute(name);
					else if (value === true)
						element.setAttribute(name, name);
					else element.setAttribute(name, value);
				}
				return element;
			},



			setOpacity: function(element, opacity) {
				element = $(element);
				element.style.opacity = (value == 1 || value === '') ? '' : (value < 0.00001) ? 0 : value;
				return element
			},


			getStyle: function(element, style) {
				element = amp.byId(element);
				style = style == 'float' ? 'cssFloat' : style;
				var value = element.style[style];

				if (!value) {
					var css = document.defaultView.getComputedStyle(element, null);
					value = css ? css[style] : null;
				}

				if (style == 'opacity')
					return value ? parseFloat(value) : 1.0;

				return value == 'auto' ? null : value;
			},

			setStyle: function(element, name, value) {
				element = amp.byId(element);
				var elementStyle = element.style, match;
				if (arguments.length == 3)
					name = { name: value };
				if (amp.isString(name)) {
					element.style.cssText += ';' + styles;
					return (styles.indexOf('opacity') > -1) ? element.setOpacity(name.match(/opacity:\s*(\d?\.?\d*)/)[1]) : element;
				}
				for (var property in name) {
					if (property == 'opacity')
						Element.setOpacity(element, name[property]);
					else {
						var prop = property;
						if (property == 'float' || property == 'cssFloat')
							prop = amp.isUndefined(elementStyle.styleFloat) ? 'cssFloat' : 'styleFloat';
						elementStyle[prop] = name[property];
					}
				}
				return element;
			}
		}


		if (amp.Browser.Opera) {
			Element.getStyle = Element.getStyle.wrap(
		function(proceed, element, style) {
				switch (style) {
					case 'left': case 'top': case 'right': case 'bottom':
						if (proceed(element, 'position') === 'static') return null;
					case 'height': case 'width':
						// returns '0px' for hidden elements; we want it to return null
						if (element.style.display == 'none') return null;

						// returns the border-box dimensions rather than the content-box
						// dimensions, so we subtract padding and borders from the value
						var dim = parseInt(proceed(element, style), 10);

						if (dim !== element['offset' + style.capitalize()])
							return dim + 'px';

						var properties;
						if (style === 'height') {
							properties = ['border-top-width', 'padding-top', 'padding-bottom', 'border-bottom-width'];
						}
						else {
							properties = ['border-left-width', 'padding-left', 'padding-right', 'border-right-width'];
						}

						var iterator = function(memo, property) {
							var val = proceed(element, property);
							return val === null ? memo : memo - parseInt(val, 10);
						}
						properties.each(function(value, index) {
							dim = iterator(dim, value, index);
						});
						return dim + "px";
					default: return proceed(element, style);
				}
			});

			Element.readAttribute = Element.readAttribute.wrap(
			function(proceed, element, attribute) {
				if (attribute === 'title') return element.title;
				return proceed(element, attribute);
			});
		} else if (amp.Browser.Msie) {

			Element.attributeTranslations = {
				read: {
					names: {
						'class': 'className',
						'for': 'htmlFor'
					},
					values: {
						_getAttr: function(element, attribute) {
							return element.getAttribute(attribute, 2);
						},
						_getAttrNode: function(element, attribute) {
							var node = element.getAttributeNode(attribute);
							return node ? node.value : "";
						},
						_getEv: function(element, attribute) {
							attribute = element.getAttribute(attribute);
							return attribute ? attribute.toString().slice(23, -2) : null;
						},
						_flag: function(element, attribute) {
							return $(element).hasAttribute(attribute) ? attribute : null;
						},
						style: function(element) {
							return element.style.cssText.toLowerCase();
						},
						title: function(element) {
							return element.title;
						}
					}
				}
			}

			Element.attributeTranslations.write = {
				names: Object.extend({
					cellpadding: 'cellPadding',
					cellspacing: 'cellSpacing'
				}, Element._attributeTranslations.read.names),
				values: {
					checked: function(element, value) {
						element.checked = !!value;
					},

					style: function(element, value) {
						element.style.cssText = value ? value : '';
					}
				}
			}


			Element.attributeTranslations.has = {};

			('colSpan rowSpan vAlign dateTime accessKey tabIndex ' +
			  'encType maxLength readOnly longDesc').split(' ').each(function(attr) {
				attributeTranslations.write.names[attr.toLowerCase()] = attr;
				attributeTranslations.has[attr.toLowerCase()] = attr;
			});

			(function(v) {
				amp.extend(v, {
					href: v._getAttr,
					src: v._getAttr,
					type: v._getAttr,
					action: v._getAttrNode,
					disabled: v._flag,
					checked: v._flag,
					readonly: v._flag,
					multiple: v._flag,
					onload: v._getEv,
					onunload: v._getEv,
					onclick: v._getEv,
					ondblclick: v._getEv,
					onmousedown: v._getEv,
					onmouseup: v._getEv,
					onmouseover: v._getEv,
					onmousemove: v._getEv,
					onmouseout: v._getEv,
					onfocus: v._getEv,
					onblur: v._getEv,
					onkeypress: v._getEv,
					onkeydown: v._getEv,
					onkeyup: v._getEv,
					onsubmit: v._getEv,
					onreset: v._getEv,
					onselect: v._getEv,
					onchange: v._getEv
				});
			})(Element.attributeTranslations.read.values);


			/*
			$w('positionedOffset viewportOffset').each(function(method) {
			Element.Methods[method] = Element.Methods[method].wrap(
			function(proceed, element) {
			element = $(element);
			var position = element.getStyle('position');
			if (position !== 'static') return proceed(element);
			// Trigger hasLayout on the offset parent so that IE6 reports
			// accurate offsetTop and offsetLeft values for position: fixed.
			var offsetParent = element.getOffsetParent();
			if (offsetParent && offsetParent.getStyle('position') === 'fixed')
			offsetParent.setStyle({ zoom: 1 });
			element.setStyle({ position: 'relative' });
			var value = proceed(element);
			element.setStyle({ position: position });
			return value;
			}
			);
			});*/

			Element.getStyle = function(element, style) {
				element = amp.byId(element);
				style = (style == 'float' || style == 'cssFloat') ? 'styleFloat' : style;
				var value = element.style[style];
				if (!value && element.currentStyle) value = element.currentStyle[style];

				if (style == 'opacity') {
					if (value = (Element.getStyle(element, 'filter') || '').match(/alpha\(opacity=(.*)\)/))
						if (value[1]) return parseFloat(value[1]) / 100;
					return 1.0;
				}

				if (value == 'auto') {
					if ((style == 'width' || style == 'height') && (Element.getStyle(element, 'display') != 'none'))
						return element['offset' + style.capitalize()] + 'px';
					return null;
				}
				return value;
			}

			Element.setOpacity = function(element, value) {
				function stripAlpha(filter) {
					return filter.replace(/alpha\([^\)]*\)/gi, '');
				}
				element = amp.byId(element);
				var currentStyle = element.currentStyle;
				if ((currentStyle && !currentStyle.hasLayout) ||
			  (!currentStyle && element.style.zoom == 'normal'))
					element.style.zoom = 1;

				var filter = Element.getStyle(element, 'filter'), style = element.style;
				if (value == 1 || value === '') {
					(filter = stripAlpha(filter)) ?
				style.filter = filter : style.removeAttribute('filter');
					return element;
				} else if (value < 0.00001) value = 0;
				style.filter = stripAlpha(filter) +
			  'alpha(opacity=' + (value * 100) + ')';
				return element;
			}



		} else if (amp.Browser.Mozilla && new RegExp("rv:1\\.8\\.0").test(navigator.userAgent)) {

			Element.setOpacity = function(element, value) {
				element = amp.byId(element);
				element.style.opacity = (value == 1) ? 0.999999 : (value === '') ? '' : (value < 0.00001) ? 0 : value;
				return element;
			}

		} else if (amp.Browser.Webkit) {
			Element.setOpacity = function(element, value) {
				element = amp.byId(element);
				element.style.opacity = (value == 1 || value === '') ? '' : (value < 0.00001) ? 0 : value;

				if (value == 1)
					if (element.tagName == 'IMG' && element.width) {
					element.width++; element.width--;
				} else try {
					var n = document.createTextNode(' ');
					element.appendChild(n);
					element.removeChild(n);
				} catch (e) { }

				return element;
			};

			// Safari returns margins on body which is incorrect if the child is absolutely
			// positioned.  For performance reasons, redefine Element#cumulativeOffset for
			// KHTML/WebKit only.
			Element.cumulativeOffset = function(element) {
				var valueT = 0, valueL = 0;
				do {
					valueT += element.offsetTop || 0;
					valueL += element.offsetLeft || 0;
					if (element.offsetParent == document.body)
						if (Element.getStyle(element, 'position') == 'absolute') break;

					element = element.offsetParent;
				} while (element);

				return new amp.UI.Offset(valueL, valueT);
			};
		}

		if (amp.Browser.Msie || amp.Browser.Opera) {
			// IE and Opera are missing .innerHTML support for TABLE-related and SELECT elements

			Element.setHtml = function(element, value) {
				var tagName = element.tagName.toUpperCase();

				if (tagName in Insertions.tags) {
					amp.array(element.childNodes).each(function(node) { element.removeChild(node) });
					Element.getContentFromAnonymousElement(tagName, scripts.strip(value))
					.each(function(node) { element.appendChild(node) });
				}
				else element.innerHTML = scripts.strip(value);
			}


		}






		var dom = function(selector) {
			this.elements = amp.isString(selector) ? amp.array($S.query(selector)) : amp.array(selector);
		}

		dom.prototype = {
			elements: new Array(),


			byId: amp.byId,

			get: function(index) {
				if (!index)
					index = 0;
				return this.getElements()[index];
			},

			getElements: function() {
				return this.elements;
			},

			update: function(value) {
				///<returns type="amp.UI.Dom.prototype"></returns>
				this.elements.invoke(Element.update, value);
				return this;
			},

			html: function(value) {
				///<returns type="amp.UI.Dom.prototype"></returns>
				if (arguments.length == 1) {
					this.elements.invoke(Element.setHtml, value);
					return this;
				}
				return this.get().innerHTML;
			},

			text: function(value) {
				///<returns type="amp.UI.Dom.prototype"></returns>
				if (arguments.length == 1) {
					this.wrapper.text(value);
					return this;
				}
				return this.wrapper.text();
			},

			each: function(callback) {
				///<returns type="amp.UI.Dom.prototype"></returns>
				this.elements.invoke(callback, arguments);
				return this;
			},

			hide: function() {
				///<returns type="amp.UI.Dom.prototype"></returns>
				this.elements.invoke(function(item) {
					$D.setVisible(item, false);
				});
				return this;
			},

			show: function() {
				///<returns type="amp.UI.Dom.prototype"></returns>
				this.elements.invoke(function(item) {
					$D.setVisible(item, true);
				});
				return this;
			},

			toggle: function() {
				///<returns type="amp.UI.Dom.prototype"></returns>
				this.elements.invoke(function(item) {
					var visible = $D.getVisible(item);
					$D.setVisible(item, !visible);
				});
				return this;
			},

			css: function(name, value) {
				///<returns type="amp.UI.Dom.prototype"></returns>
				var el = this.get();
				if (arguments.length == 1 && amp.isString(name)) {
					return Element.getStyle(el, name);
				} else if (arguments.length == 1) {
					this.elements.invoke(Element.setStyle, name);
					return this;
				} else
					this.elements.invoke(Element.setStyle, name, value);
				return this;
			},

			height: function() {
				return $D.getBounds(this.get()).height;
			},

			width: function() {
				return $D.getBounds(this.get()).width;
			},

			add: function(selector) {
				///<returns type="amp.UI.Dom.prototype"></returns>
				this.elements.concat(amp.isString(selector) ? amp.array($S.query(selector)) : selector);
				return this;
			},

			remove: function(items) {
				///<returns type="amp.UI.Dom.prototype"></returns>
				this.elements.remove(items);
				return this;
			},

			addClass: function(names) {
				///<returns type="amp.UI.Dom.prototype"></returns>
				names = names.split(" ");
				for (var i; i < names.length; i++)
					this.elements.invoke($D.addCssClass, names[i]);
				return this;
			},

			removeClass: function(names) {
				///<returns type="amp.UI.Dom.prototype"></returns>
				names = names.split(" ");
				for (var i; i < names.length; i++)
					this.elements.invoke($D.removeCssClass, names[i]);
				return this;
			},

			hasClass: function(name) {
				return $D.containsCssClass(this.get(), name);
			},

			toggleClass: function(name) {
				///<returns type="amp.UI.Dom.prototype"></returns>
				this.elements.invoke($D.toggleCssClass, name);
				return this;
			},

			next: function(selector) {
				///<returns type="amp.UI.Dom.prototype"></returns>
				var next = Element.nextSiblings(this.get());
				if (arguments.length) {
					if (amp.isNumber(selector))
						return amp(next[0]);
					return amp($S.filter(next, selector));
				}
				return amp(next);
			},

			previous: function(selector) {
				///<returns type="amp.UI.Dom.prototype"></returns>
				var previous = Element.previousSiblings(this.get());
				if (arguments.length) {
					if (amp.isNumber(selector))
						return amp(previous[0]);
					return amp($S.filter(previous, selector));
				}
				return amp(previous);
			},

			insert: function(insertions) {
				///<returns type="amp.UI.Dom.prototype"></returns>
				this.elements.invoke(Element.insert, insertions);
				return this;
			},

			insertInto: function(element, position) {
				this.elements.each(function(item) {
					var insertion = {};
					insertion[position] = item;
					Element.insert(element, insertion);
				});
			},

			up: function(selector) {
				///<returns type="amp.UI.Dom.prototype"></returns>
				if (arguments.length) {
					if (amp.isNumber(selector))
						return amp(Element.ancestors(this.get())[selector]);
					return amp($S.filter(Element.ancestors(this.get()), selector));
				}
				return amp(this.get().parentNode);
			},

			parents: function(selector) {
				///<returns type="amp.UI.Dom.prototype"></returns>
				return amp(Element.ancestors(this.get()));
			},

			children: function(selector) {
				///<returns type="amp.UI.Dom.prototype"></returns>
				return amp(Element.decendants(this.get()));
			},

			down: function(selector) {
				///<returns type="amp.UI.Dom.prototype"></returns>
				if (arguments.length) {
					if (amp.isNumber(selector))
						return amp(Element.descendants(this.get())[selector]);
					return amp($S.filter(Element.descendants(this.get()), selector));
				}
				return amp(Element.firstDescendant(this.get()));
			},

			siblings: function(selector) {
				///<returns type="amp.UI.Dom.prototype"></returns>
				return amp(this.wrapper.siblings(selector).get());
			},

			replace: function(content) {
				///<returns type="amp.UI.Dom.prototype"></returns>
				this.elements.invoke(Element.replace, content);
				return this;
			},

			filter: function(selector) {
				///<returns type="amp.UI.Dom.prototype"></returns>
				this.elements = $S.filter(this.elements, selector);
				return this;
			},

			attr: function(name, value) {
				///<returns type="amp.UI.Dom.prototype"></returns>
				var el = this.get();
				if (arguments.length == 1 && amp.isString(name)) {
					return Element.readAttribute(el, name);
				} else if (arguments.length == 1) {
					this.elements.invoke(function(item, attrs) {
						for (var property in attrs)
							Element.writeAttribute(el, property, attr[property]);
					}, name);
					return this;
				} else {
					Element.writeAttribute(el, name, value);
					return this;
				}
			},

			create: function(tagName, attrs) {
				///<returns type="amp.UI.Dom.prototype"></returns>
				return amp(Element.create(tagName, attrs));
			}


		}



		amp["class"]("amp.UI.Dom", null, dom);

		amp.create = function(tagName, attrs) {
			///<returns type="amp.UI.Dom.prototype"></returns>	
			return amp(Element.create(tagName, attrs));
		}

		amp.prototype.constructor = function(selector) {
			///<returns type="amp.UI.Dom.prototype"></returns>		
			return new amp.UI.Dom(selector);
		}

		amp.find = function(selector) {
			///<returns type="amp.Dom.prototype"></returns>	
			return new amp.UI.Dom(selector);
		}

		amp.findById = function(selector) {
			///<returns type="amp.Dom.prototype"></returns>	
			return new amp.UI.Dom(amp.byId(selector));
		}



	})()